#pragma once
#include <iostream>
#include <string>
#include <cstdio>
#include <set>

#include "Log.hpp"

const std::string sep = " ";

class Command
{
private:
    // 从文件中加载白名单
    void Load(const std::string &path)
    {
        // 打开文件
        std::ifstream in(path);

        // 打开失败
        if (!in.is_open())
        {
            LOG(FATAL, "file open error, filename:%s\n", path.c_str());
            return;
        }

        // 读文件
        std::string line;
        while (std::getline(in, line))
        {
            LOG(DEBUG, "load command [%s] success\n", line.c_str());
            _safe_cmd.insert(line);
        }

        // 关闭文件
        in.close();
    }

public:
    Command(const std::string &cmd_path)
        : _cmd_path(cmd_path)
    {
        Load(cmd_path);
    }

    // 提取命令的前缀（不要命令的选项）
    std::string PrefixCommand(std::string cmd)
    {
        // 命令为空
        if (cmd.empty())
            return std::string();

        auto pos = cmd.find(sep);
        if (pos == std::string::npos)
            return cmd; // 无选项
        else
            return cmd.substr(0, pos);
    }
    bool SafeCheck(const std::string &cmd) // 检查该命令是否安全
    {
        std::string prefix = PrefixCommand(cmd);

        auto iter = _safe_cmd.find(prefix);

        // 不在白名单内
        if (iter == _safe_cmd.end())
            return false;

        return true;
    }
    // 执行命令，并返回结果
    std::string Excute(const std::string &cmd)
    {
        std::string result;

        // 检查是否为白名单内的命令
        if (SafeCheck(cmd))
        {
            FILE *fp = popen(cmd.c_str(), "r"); // 执行命令

            if (fp == nullptr)
                return "failed";

            char buffer[1024];

            // 读取执行结果
            while (fgets(buffer, sizeof(buffer), fp) != NULL)
            {
                result += buffer;
            }

            pclose(fp);
        }
        else
        {
            result = "cmd illegal\n";
        }
        return result;
    }

    ~Command()
    {
    }

private:
    std::string _cmd_path;           // 名单路径
    std::set<std::string> _safe_cmd; // 白名单
};